home *** CD-ROM | disk | FTP | other *** search
- (* 12/14/83 - Time out on first packet added *)
- (* 12/14/83 - MTS system calls silenced *)
- (* 12/05/83 - Carriage control option implemented *)
- (* 12/03/83 - Tape mode and IBM mode established *)
- (* 11/21/83 - Program commented *)
- (* 11/19/83 - History line begun *)
- (* 11/16/83 - complete working version in place *)
-
- PROGRAM kermit;
- (*
- KERMIT file transfer utility for the Michigan Terminal System (MTS).
- Version 1.0 written by William S. Hall, Mathematical Reviews,
- Ann Arbor, MI in PASCAL/VS.
-
- For program usage and limitations see SJ1K:kermit.doc
- *)
- %page
- CONST
- (*
- Ordinal values of control characters. Where values differ between
- the EBCDEC and ASCII control characters, then are so noted.
- *)
- NUL = 00; SOH = 01; STX = 2; ETX = 03;
- EOT = 55; (* A/E = 04/55 *)
- ENQ = 45; (* A/E = 05/45 *)
- ACK = 46; (* A/E = 06/46 *)
- BEL = 47; (* A/E = 07/47 *)
- BS = 22; (* A/E = 08/22 *)
- HT = 05; (* A/E = 09/05 *)
- LF = 37; (* A/E = 10/37 *)
- VT = 11; FF = 12; CR = 13; SO = 14;
- SI = 15; DLE = 16; DC1 = 17; DC2 = 18;
- DC3 = 19;
- DC4 = 60; (* A/E = 20/60 *)
- NAK = 61; (* A/E = 21/61 *)
- SYN = 50; (* A/E = 22/50 *)
- ETB = 38; (* A/E = 23/38 *)
- CAN = 24;
- EM = 25;
- SUB = 63; (* A/E = 26/63 *)
- ESC = 39; (* A/E = 27/39 *)
- FS = 28;
- GS = 29;
- RS = 30;
- US = 31;
- SP = 64; (* A/E = 32/64 *)
- DEL = 7; (* A/E = 127/7 *)
-
- (* Other program constants needed in the program *)
- MAXPACK = 94; (* Maximum packet size *)
- MAXTRY = 5; (* Times to retry a packet *)
- MYQUOTE = '#'; (* Quote character I will use *)
- MYPAD = 0; (* Number of padding characters I need *)
- MYPCHAR = NUL; (* Ordinal value of padding character I need *)
- MYEOL = CR; (* Ordinal value of end of line char I need *)
- MYTIME = 5; (* Seconds after which I should be timed out *)
- NAMESIZE = 40; (* Maximum size of file name *)
- MAXFILES = 20; (* Maximum number of files to send *)
- SNDINIT_DLY = 8000000; (* Delay in microseconds before first packet *)
- %page
- TYPE
- (* These types are used to call MTS procedures *)
- char255 = packed array[1..255] of char;
- halfword = packed -32768..32767;
- (* This type holds a packet being received or sent *)
- packet_type = packed array[1..MAXPACK] of char;
- (* This points to a packet *)
- packet_ptr = @packet_type;
- (* Timeout variable for system time-out call *)
- intpair = array[1..2] of integer;
-
- VAR
- date : alfa; (* used for running date and time call *)
- time : alfa;
- delay : intpair; (* used for calling twait procedure *)
- cc : boolean; (* Carriage control char in column 1? *)
- ccinfo : char; (* used to set value of cc from input *)
- col : integer; (* Marks column position *)
- cmdstr : char255; (* used to issue commands to MTS *)
- ascii : boolean; (* ascii char set in use *)
- i : integer; (* Utility integer *)
- size : integer; (* Size of present data *)
- n : integer; (* Message number *)
- rpsiz : integer; (* Maximum receive packet size *)
- spsiz : integer; (* Maximum send packet size *)
- pad : integer; (* How much padding to send *)
- timint : integer; (* Timeout for foreign host on sends *)
- numtry : integer; (* Times this packet tried *)
- oldtry : integer; (* Times previous packet retried *)
- debug : boolean; (* true means debugging *)
- state : char; (* Present state of the automaton *)
- padchar : char; (* Padding character to send *)
- eol : char; (* End of line character to send *)
- quote : char; (* Quote character in incoming data *)
- recpkt : packet_ptr; (* Receive packet buffer pointer *)
- packet : packet_ptr; (* Send packet buffer pointer *)
- command : char; (* Command - receive or send *)
- filnam : array[1..MAXFILES] of string(NAMESIZE); (* holds file names *)
- nfiles : integer; (* number of files to send *)
- numsent : integer; (* number already send *)
- bugfil : text; (* debug file *)
- sndfil : text; (* file to be sent *)
- rcvfil : text; (* file to be received *)
- %page
- PROCEDURE cmdnoe(const cmd : char255; const len : halfword); fortran;
- (* Makes MTS calls *)
-
- PROCEDURE twait(const code : integer; const val : intpair); fortran;
- (* Executes delays *)
-
- PROCEDURE setsys;
- (*
- Set the terminal for file transfer so that no packets are wrapped
- and the terminal is not paged. Also MTS must not echo characters
- during the transfer, and control characters, especially control A,
- must be allowed to pass unintercepted by the front end (Hermes).
- Finally, reader mode allows XON-XOFF flow control.
- *)
- BEGIN
- cmdnoe('$control *msink* width=255', 26);
- cmdnoe('$control *msink* outlen=255', 27);
- cmdnoe('$control *msink* reader=on', 26);
- cmdnoe('$control *msink* echo=off', 25);
- cmdnoe('$control *msink* npc=off', 24);
- cmdnoe('$control *msink* pagewait=off', 29);
- END; {setsys}
-
- PROCEDURE resetsys;
- (* Restore the user's system after completion of run *)
- BEGIN
- cmdnoe('$control *msink* reset', 22);
- END; {resetsys}
-
- FUNCTION toupper(c : char) : char;
- (* Convert lower to upper case *)
- BEGIN
- if ((c >= 'a') and (c <= 'i')) or ((c >= 'j') and (c <= 'r'))
- or ((c >= 's') and (c <= 'z')) then
- BEGIN
- if ascii
- then toupper := chr(ord(c) - 32)
- else
- toupper := chr(ord(c) + 64)
- END
- else toupper := c;
- END; {toupper}
-
- FUNCTION checksum(c : INTEGER) : INTEGER; (* checksum based on ASCII sum *)
- (*
- Compute a checksum in the range 0 to 63. This is a Pascal version
- of the formula (sum + (sum & 192) div 64) & 63, where & is bitwise 'and'
- *)
- VAR
- x : INTEGER;
- BEGIN
- x := (c MOD 256) DIV 64;
- x := x + c;
- checksum := x MOD 64;
- END; {checksum}
- %page
- FUNCTION tochar(ch : integer) : char;
- (*
- Converts an integer in the range 0 to 94 to a printing character.
- If ASCII is the underlying character set, this is trivial. For
- EBCDEC, the internal representation of characters in Pascal/VS,
- a case statement is appropriate. Note that three characters,
- namely, "^", "`", and "\" cannot be represented in quotes and
- chr(ordinal value) is used instead. This seems to be a pecularity
- of the MTS operating system and not EBCDEC in general.
- *)
- BEGIN
- if ascii then
- tochar := chr(ch + 32)
- else case ch of
- 0 : tochar := ' '; 1 : tochar := '!'; 2 : tochar := '"';
- 3 : tochar := '#'; 4 : tochar := '$'; 5 : tochar := '%';
- 6 : tochar := '&'; 7 : tochar := ''''; 8 : tochar := '(';
- 9 : tochar := ')'; 10 : tochar := '*'; 11 : tochar := '+';
- 12 : tochar := ','; 13 : tochar := '-'; 14 : tochar := '.';
- 15 : tochar := '/'; 16 : tochar := '0'; 17 : tochar := '1';
- 18 : tochar := '2'; 19 : tochar := '3'; 20 : tochar := '4';
- 21 : tochar := '5'; 22 : tochar := '6'; 23 : tochar := '7';
- 24 : tochar := '8'; 25 : tochar := '9'; 26 : tochar := ':';
- 27 : tochar := ';'; 28 : tochar := '<'; 29 : tochar := '=';
- 30 : tochar := '>'; 31 : tochar := '?'; 32 : tochar := '@';
- 33 : tochar := 'A'; 34 : tochar := 'B'; 35 : tochar := 'C';
- 36 : tochar := 'D'; 37 : tochar := 'E'; 38 : tochar := 'F';
- 39 : tochar := 'G'; 40 : tochar := 'H'; 41 : tochar := 'I';
- 42 : tochar := 'J'; 43 : tochar := 'K'; 44 : tochar := 'L';
- 45 : tochar := 'M'; 46 : tochar := 'N'; 47 : tochar := 'O';
- 48 : tochar := 'P'; 49 : tochar := 'Q'; 50 : tochar := 'R';
- 51 : tochar := 'S'; 52 : tochar := 'T'; 53 : tochar := 'U';
- 54 : tochar := 'V'; 55 : tochar := 'W'; 56 : tochar := 'X';
- 57 : tochar := 'Y'; 58 : tochar := 'Z'; 59 : tochar := '[';
- 60 : tochar := chr(186);
- 61 : tochar := ']';
- 62 : tochar := chr(170);
- 63 : tochar := '_';
- 64 : tochar := chr(154);
- 65 : tochar := 'a';
- 66 : tochar := 'b'; 67 : tochar := 'c'; 68 : tochar := 'd';
- 69 : tochar := 'e'; 70 : tochar := 'f'; 71 : tochar := 'g';
- 72 : tochar := 'h'; 73 : tochar := 'i'; 74 : tochar := 'j';
- 75 : tochar := 'k'; 76 : tochar := 'l'; 77 : tochar := 'm';
- 78 : tochar := 'n'; 79 : tochar := 'o'; 80 : tochar := 'p';
- 81 : tochar := 'q'; 82 : tochar := 'r'; 83 : tochar := 's';
- 84 : tochar := 't'; 85 : tochar := 'u'; 86 : tochar := 'v';
- 87 : tochar := 'w'; 88 : tochar := 'x'; 89 : tochar := 'y';
- 90 : tochar := 'z'; 91 : tochar := '{'; 92 : tochar := '|';
- 93 : tochar := '}'; 94 : tochar := '~';
- otherwise
- if debug then writeln(bugfil, 'tochar error');
- END; {case}
- END; {tochar}
- %page
- FUNCTION unchar(ch : char) : integer; (* Undoes tochar *)
- (*
- Converts a printing character to an integer in the range 0-94.
- This procedure undoes the action of "tochar".
- *)
- BEGIN
- if ascii then
- unchar := ord(ch) - 32
- else case ch of
- ' ' : unchar := 0; '!' : unchar := 1; '"' : unchar := 2;
- '#' : unchar := 3; '$' : unchar := 4; '%' : unchar := 5;
- '&' : unchar := 6; '''': unchar := 7; '(' : unchar := 8;
- ')' : unchar := 9; '*' : unchar := 10; '+' : unchar := 11;
- ',' : unchar := 12; '-' : unchar := 13; '.' : unchar := 14;
- '/' : unchar := 15; '0' : unchar := 16; '1' : unchar := 17;
- '2' : unchar := 18; '3' : unchar := 19; '4' : unchar := 20;
- '5' : unchar := 21; '6' : unchar := 22; '7' : unchar := 23;
- '8' : unchar := 24; '9' : unchar := 25; ':' : unchar := 26;
- ';' : unchar := 27; '<' : unchar := 28; '=' : unchar := 29;
- '>' : unchar := 30; '?' : unchar := 31; '@' : unchar := 32;
- 'A' : unchar := 33; 'B' : unchar := 34; 'C' : unchar := 35;
- 'D' : unchar := 36; 'E' : unchar := 37; 'F' : unchar := 38;
- 'G' : unchar := 39; 'H' : unchar := 40; 'I' : unchar := 41;
- 'J' : unchar := 42; 'K' : unchar := 43; 'L' : unchar := 44;
- 'M' : unchar := 45; 'N' : unchar := 46; 'O' : unchar := 47;
- 'P' : unchar := 48; 'Q' : unchar := 49; 'R' : unchar := 50;
- 'S' : unchar := 51; 'T' : unchar := 52; 'U' : unchar := 53;
- 'V' : unchar := 54; 'W' : unchar := 55; 'X' : unchar := 56;
- 'Y' : unchar := 57; 'Z' : unchar := 58; '[' : unchar := 59;
- chr(186) : unchar := 60;
- ']' : unchar := 61;
- chr(170) : unchar := 62;
- '_' : unchar := 63;
- chr(154) : unchar := 64;
- 'a' : unchar := 65;
- 'b' : unchar := 66; 'c' : unchar := 67; 'd' : unchar := 68;
- 'e' : unchar := 69; 'f' : unchar := 70; 'g' : unchar := 71;
- 'h' : unchar := 72; 'i' : unchar := 73; 'j' : unchar := 74;
- 'k' : unchar := 75; 'l' : unchar := 76; 'm' : unchar := 77;
- 'n' : unchar := 78; 'o' : unchar := 79; 'p' : unchar := 80;
- 'q' : unchar := 81; 'r' : unchar := 82; 's' : unchar := 83;
- 't' : unchar := 84; 'u' : unchar := 85; 'v' : unchar := 86;
- 'w' : unchar := 87; 'x' : unchar := 88; 'y' : unchar := 89;
- 'z' : unchar := 90; '{' : unchar := 91; '|' : unchar := 92;
- '}' : unchar := 93; '~' : unchar := 94;
- otherwise
- if debug then writeln(bugfil, 'unchar error');
- END; {case}
- END; {unchar}
- %page
- FUNCTION ctl(ch : char) : char;
- (*
- Changes the printing characters shown below to control characters.
- Used to unquote a quoted control character in a packet.
- *)
- BEGIN
- if ascii then
- ctl := chr(ord(ch) - 64)
- else case ch of
- '@' : ctl := chr(NUL); 'A' : ctl := chr(SOH);
- 'B' : ctl := chr(STX); 'C' : ctl := chr(ETX);
- 'D' : ctl := chr(EOT); 'E' : ctl := chr(ENQ);
- 'F' : ctl := chr(ACK); 'G' : ctl := chr(BEL);
- 'H' : ctl := chr(BS); 'I' : ctl := chr(HT);
- 'J' : ctl := chr(LF); 'K' : ctl := chr(VT);
- 'L' : ctl := chr(FF); 'M' : ctl := chr(CR);
- 'N' : ctl := chr(SO); 'O' : ctl := chr(SI);
- 'P' : ctl := chr(DLE); 'Q' : ctl := chr(DC1);
- 'R' : ctl := chr(DC2); 'S' : ctl := chr(DC3);
- 'T' : ctl := chr(DC4); 'U' : ctl := chr(NAK);
- 'V' : ctl := chr(SYN); 'W' : ctl := chr(ETB);
- 'X' : ctl := chr(CAN); 'Y' : ctl := chr(EM);
- 'Z' : ctl := chr(SUB); '[' : ctl := chr(ESC);
- chr(186) : ctl := chr(FS);
- ']' : ctl := chr(GS);
- chr(170) : ctl := chr(RS);
- '_' : ctl := chr(US);
- '?' : ctl := chr(DEL);
- otherwise
- if debug then writeln(bugfil, 'ctl error');
- END; {case}
- END; {ctl}
- %page
- FUNCTION unctl(ch : char) : char;
- (* Changes a control character to its corresponding printing form *)
- VAR
- i : integer;
- BEGIN
- i := ord(ch);
- if ascii then
- unctl := chr(i + 64)
- else case i of
- NUL : unctl := '@'; SOH : unctl := 'A';
- STX : unctl := 'B'; ETX : unctl := 'C';
- EOT : unctl := 'D'; ENQ : unctl := 'E';
- ACK : unctl := 'F'; BEL : unctl := 'G';
- BS : unctl := 'H'; HT : unctl := 'I';
- LF : unctl := 'J'; VT : unctl := 'K';
- FF : unctl := 'L'; CR : unctl := 'M';
- SO : unctl := 'N'; SI : unctl := 'O';
- DLE : unctl := 'P'; DC1 : unctl := 'Q';
- DC2 : unctl := 'R'; DC3 : unctl := 'S';
- DC4 : unctl := 'T'; NAK : unctl := 'U';
- SYN : unctl := 'V'; ETB : unctl := 'W';
- CAN : unctl := 'X'; EM : unctl := 'Y';
- SUB : unctl := 'Z'; ESC : unctl := '[';
- FS : unctl := chr(186);
- GS : unctl := ']';
- RS : unctl := chr(170);
- US : unctl := '_';
- DEL : unctl := '?';
- otherwise
- if debug then writeln(bugfil, 'unctl error');
- END; {case}
- END; {unctl}
- %page
- FUNCTION aord(ch : char) : integer;
- (* Convert a character to its ASCII ordinal value *)
- BEGIN
- if ascii then aord := ord(ch)
- else aord := unchar(ch) + 32;
- END; {aord}
-
- FUNCTION writeopn(nampkt : packet_ptr; len : integer) : boolean;
- (*
- Open a file for writing during receive mode. The filename itself
- is obtained from the sending Kermit in a file name packet. The
- name is extracted and concatenated to dynamically create and open
- it. Pascal/VS does not presently return error codes, but by
- declaring the function as boolean, this feature can be readily
- implemented when return codes become available. Use of column
- 1 for carriage control is an option.
- *)
- VAR
- filnam : string(NAMESIZE);
- crname : string(NAMESIZE + 20);
- BEGIN
- filnam := substr(str(nampkt@), 1, len);
- crname := '$create '||filnam;
- cmdnoe(crname, length(crname));
- if debug then writeln(bugfil, 'Opening ', filnam);
- if cc then
- rewrite(rcvfil, 'FILE='||filnam|| ' MAXLEN=255 ')
- else
- rewrite(rcvfil, 'FILE='||filnam|| ' MAXLEN=255 NOCC');
- col := 1;
- writeopn := true;
- END; {writeopn}
-
- FUNCTION getnxt : boolean;
- (*
- Gen next file for reading when in send mode. No error codes are
- returned by Pascal/VS at present, but the function returns a
- boolean value, allowing implementation of such when available.
- *)
- BEGIN
- if debug then writeln(bugfil, 'Opening ', filnam[numsent]);
- reset(sndfil, 'FILE='||filnam[numsent]||' MAXLEN=255');
- col := 1;
- getnxt := true;
- END; {getnxt}
- %page
- PROCEDURE rpar(data : packet_ptr);
- (* Get the other side's sent-init packet. The time-out is N/A *)
- BEGIN
- spsiz := unchar(data@[1]); (* Maximum send packet size *)
- timint := unchar(data@[2]); (* When I should time out *)
- pad := unchar(data@[3]); (* Number of pads to send *)
- padchar := ctl(data@[4]); (* padding char to send *)
- eol := chr(unchar(data@[5])); (* end-of-line char to send *)
- quote := data@[6]; (* incoming data quote char *)
- if debug then (* write this to trace file *)
- writeln(bugfil, 'sendinit data from other side - ',
- spsiz:3, timint:3, pad:3, ord(padchar):3,
- ord(eol):3, quote);
- END; {rpar}
-
- PROCEDURE spar(data : packet_ptr);
- (* Fill data array with my send-init parameters *)
- BEGIN
- data@[1] := tochar(MAXPACK); (* my max packet size *)
- data@[2] := tochar(MYTIME); (* when I should be timed out *)
- data@[3] := tochar(MYPAD); (* how much padding I need *)
- data@[4] := unctl(chr(MYPCHAR)); (* my pad char *)
- data@[5] := tochar(MYEOL); (* my end of line *)
- data@[6] := MYQUOTE; (* quote char I send *)
- END; {spar}
- %page
- FUNCTION bufill(bufptr : packet_ptr) : integer;
- (*
- Get a buffer full of data from the file that is being sent.
- Control characters are quoted (preceded by a '#').
- *)
- VAR
- i : integer; (* loop index *)
- t : char; (* utility character *)
- BEGIN
- i := 1;
- while (not eof(sndfil)) and ( i < spsiz - 8) do
- (* spsiz - 8 keeps the buffer from overflowing *)
- BEGIN
- if eoln(sndfil) then (* end of line. Quote CR and LF *)
- BEGIN
- (* quote the char *) bufptr@[i] := quote;
- (* uncontrollify it *) bufptr@[i + 1] := unctl(chr(CR));
- (* do the same for *) bufptr@[i + 2] := quote;
- (* the line feed *) bufptr@[i + 3] := unctl(chr(LF));
- (* bump loop ctr *) i := i + 4;
- readln(sndfil); (* reset file pointer *)
- col := 1; (* reset column position *)
- END {if}
- else
- BEGIN
- read(sndfil,t); (* get the next char *)
- if ((col = 1) and cc) then
- BEGIN
- if t = '1' then (* ignore unless FF *)
- BEGIN
- (* quote the form feed *) bufptr@[i] := quote;
- (* put char in buffer *) bufptr@[i + 1] := unctl(chr(FF));
- (* bump counter *) i := i + 2;
- END
- END {col = 1}
- (* control char or *) else if (ord(t) < SP) or (t = chr(DEL))
- or (t = quote) then
- (* quote? *) BEGIN
- (* yes, so quote it *) bufptr@[i] := quote;
- (* uncontrollify it *) if t <> quote then t := unctl(t);
- (* put char in buffer *) bufptr@[i + 1] := t;
- (* bump counter *) i := i + 2;
- END
- else
- BEGIN
- bufptr@[i] := t; (* put char in buffer *)
- i := i + 1; (* bump counter *)
- END;
- col := col + 1; (* advance column counter *)
- END; {else}
- END; {while}
- bufill := i - 1; (* return count *)
- END; {bufill}
- %page
- PROCEDURE bufemp(buffer : packet_ptr; len : integer);
- (* Get data from incoming packet into a file *)
- VAR
- i : integer; (* counter *)
- t : char; (* utility character *)
- BEGIN
- i := 1;
- while i <= len do (* loop thru character field *)
- BEGIN
- t := buffer@[i]; (* get character *)
- if t = MYQUOTE then (* next char must be unquoted *)
- BEGIN
- i := i + 1; (* bump counter *)
- t := buffer@[i]; (* get quoted char *)
- case t of
- (* it was a real quote *) MYQUOTE : write(rcvfil, t);
- (* CR, so assume newline *) 'M' : begin
- writeln(rcvfil);
- (* reset column marker *) col := 1;
- end;
- (* LF, don't pass *) 'J' : ;
- (* FF, so make new page *) 'L' : begin
- page(rcvfil);
- col := col + 1;
- end;
- (* expand the tabs *) 'I' : repeat
- (* assume stops at 1, 9, 17, etc. *) write(rcvfil, ' ');
- col := col + 1;
- until (col mod 8 = 1);
- otherwise
- (* make a control character *) begin
- write(rcvfil, ctl(t));
- (* increment column marker *) col := col + 1;
- end;
- END; {case}
- END {if}
- else
- begin
- write(rcvfil, t); (* put character into file *)
- col := col + 1; (* increment column marker *)
- end;
- i := i + 1;
- END; {while}
- END; {bufemp}
- %page
- FUNCTION rpack(var len, num : integer; data : packet_ptr) : char;
- (* Read a packet being sent. Compute check sum, return packet type *)
- LABEL 10; (* Heavens! a GOTO - for resynchronization *)
- VAR
- i, chksum : integer; (* counter, check sum *)
- done : boolean; (* packet read if true *)
- t, class : char; (* utility char, packet type *)
- BEGIN
- if debug then writeln(bugfil, 'rpack'); (* debug, trace file *)
- while t <> chr(SOH) do read(t); (* look for synch char SOH *)
- if debug then write(bugfil, t); (* save in debugging file *)
- done := false; (* not yet done *)
- 10: while not done do
- BEGIN
- read(t); (* get char *)
- if debug then write(bugfil, t); (* save in trace file *)
- if t = chr(SOH) then goto 10; (* if synch, start again *)
- chksum := aord(t); (* accumulate check sum *)
- len := unchar(t) - 3; (* get length of packet *)
-
- read(t); (* get char *)
- if debug then write(bugfil, t); (* save in trace file *)
- if t = chr(SOH) then goto 10; (* resynchronize *)
- chksum := chksum + aord(t); (* accumulate check sum *)
- num := unchar(t); (* get packet number *)
-
- read(t); (* get char *)
- if debug then write(bugfil, t); (* save in trace file *)
- if t = chr(SOH) then goto 10; (* resynchronize *)
- chksum := chksum + aord(t); (* accumulate sum *)
- class := t; (* get packet type *)
-
- for i := 1 to len do (* get the actual data *)
- BEGIN
- (* get char *) read(t);
- (* save in trace file *) if debug then write(bugfil, t);
- (* resynchronize *) if t = chr(SOH) then goto 10;
- (* accumulate check sum *) chksum := chksum + aord(t);
- (* store data *) data@[i] := t;
- END;
-
- read(t); (* get sender's check sum *)
- (* resynchronize *) if t = chr(SOH) then goto 10;
- (* save in trace *) if debug then write(bugfil, t);
- done := true; (* end of packet *)
- END; {while}
- if t = tochar(checksum(chksum)) then rpack := class else
- rpack := 'E'; (* compare check sums, return 'E' if bad *)
- if debug then writeln(bugfil); (* flush line to trace file *)
- END; {rpack}
- %page
- PROCEDURE spack(class : char; num, len : integer; data : packet_ptr);
- (* Send a packet to the other side *)
- TYPE
- buffer = packed array[1..100] of char;
- VAR
- i : integer; (* counter *)
- chksum : integer; (* packet checksum *)
- bufp : @buffer; (* pointer to buffer *)
- BEGIN
- if debug then writeln(bugfil, 'spack'); (* save in trace *)
- if pad > 0 then (* send padding if needed *)
- for i := 1 to pad do write(padchar);
- new(bufp); (* make space *)
- bufp@[1] := chr(SOH); (* synch character *)
- bufp@[2] := tochar(len + 3); (* char representation of length *)
- chksum := aord(bufp@[2]); (* char representation of check sum *)
- bufp@[3] := tochar(num); (* char representation of packet number *)
- chksum := chksum + aord(bufp@[3]); (* accumulate check sum *)
- bufp@[4] := class; (* packet type *)
- chksum := chksum + aord(class); (* accumulate check sum *)
- for i := 1 to len do (* accumulate data and check sum *)
- BEGIN
- bufp@[4 + i] := data@[i];
- chksum := chksum + aord(data@[i]);
- END;
- bufp@[len + 4 + 1] := tochar(checksum(chksum));
- (* char representation of check sum *)
- bufp@[len + 4 + 2] := eol; (* end of line wanted by other end *)
- for i := 1 to (len+4+1) do write(bufp@[i]);
- (* send it out to other side *)
- writeln(bufp@[len+4+2]); (* IMPORTANT! Must flush output in MTS *)
- if debug then (* save the packet in the trace file *)
- BEGIN
- for i := 1 to (len+4+2) do write(bugfil, bufp@[i]);
- writeln(bugfil); (* flush to file *)
- END;
- END; {spack}
- %page
- FUNCTION recsw : boolean;
- (* State table switcher for receiving files *)
- VAR
- done : boolean; (* no more files to receive if true *)
-
- FUNCTION rinit : char;
- (* Receive initialization from sender *)
- VAR
- len, num : integer; (* packet length, number *)
- BEGIN
- if debug then writeln(bugfil, 'rinit');
- if numtry > MAXTRY then (* too many tries, so abort *)
- rinit := 'A'
- else
- BEGIN
- (* bump try count *) numtry := numtry + 1;
- (* get a packet *) case rpack(len, num, recpkt) of
- (* got a send-init *) 'S' : BEGIN
- (* retrieve parameters from sender *) rpar(recpkt);
- (* fill up packet with my info *) spar(packet);
- (* ACK with my packet *) spack('Y', n, 6, packet);
- (* save old try count *) oldtry := numtry;
- (* start a new counter *) numtry := 0;
- (* bump count, mod 64 *) n := (n + 1) mod 64;
- (* return file-send state *) rinit := 'F';
- END; {S}
- (* didn't get packet *) 'E' : rinit := state; (* keep waiting *)
- (* some other type, abort *) otherwise
- rinit := 'A';
- END; {case}
- END; {else}
- END; {rinit}
- %page
- FUNCTION rfile : char;
- (* Receive file name *)
- VAR
- num, len : integer; (* packet number, length *)
- k : integer; (* utility integer *)
- BEGIN
- if debug then writeln(bugfil, 'rfile');
- if numtry > MAXTRY then (* abort if too many tries *)
- rfile := 'A'
- else
- BEGIN
- (* bump count *) numtry := numtry + 1;
- (* get a packet *) case rpack(len, num, recpkt) of
- (* send-init, maybe ACK *) 'S' : BEGIN
- (* has been lost *) if oldtry > MAXTRY then
- (* if too many tries, abort *) rfile := 'A'
- else
- BEGIN
- (* bump oldtry count as well *) oldtry := oldtry + 1;
- (* previous packet mod 64 ? *) k := n - 1;
- if k < 0 then k := 63;
- (* yes, so ACK it again *) if num = k then
- BEGIN
- (* send our send-init packet *) spar(packet);
- spack('Y', num,
- 6, packet);
- (* reset try counter *) numtry := 0;
- (* stay in this state *) rfile := state;
- END
- else
- (* not previous packet, abort *) rfile := 'A';
- END; {else}
- END; {S}
- (* end-of-file *) 'Z' : BEGIN
- if oldtry > MAXTRY then
- rfile := 'A'
- else
- BEGIN
- oldtry := oldtry + 1;
- (* previous packet, mod 64 ? *) k := n - 1;
- if k < 0 then k := 63;
- (* yes, so ACK it again *) if num = k then
- BEGIN
- spack('Y', num, 0,
- packet);
- numtry := 0;
- (* stay in this state *) rfile := state;
- END
- else
- (* not previous packet, abort *) rfile := 'A';
- END
- END; {Z}
- (* file-header *) 'F' : BEGIN
- (* what we really want so the *) if num <> n then
- (* packet number must be correct *) rfile := 'A'
- else
- BEGIN
- (* try to open a new file *) if not writeopn(recpkt, len) then
- rfile := 'A'
- else
- (* if OK then *) BEGIN
- (* ACK the file header *) spack('Y', n, 0, packet);
- (* reset counters *) oldtry := numtry;
- numtry := 0;
- (* bump packet number mod 64 *) n := (n + 1) mod 64;
- (* switch to data packet *) rfile := 'D';
- END;
- END;
- END; {F}
- (* break transmission *) 'B' : BEGIN
- (* need correct packet number *) if num <> n then
- rfile := 'A'
- else
- BEGIN
- (* say OK *) spack('Y', n, 0, packet);
- (* switch to complete state *) rfile := 'C';
- END;
- END; {B}
- (* souldn't get packet *) 'E' : rfile := state; (* keep trying *)
- (* something else, abort *) otherwise
- rfile := 'A';
- END; {case}
- END;
- END; {rfile}
-
- FUNCTION rdata : char;
- (* Receive data *)
- VAR
- num, len : integer; (* packet number, length *)
- k : integer; (* utility integer *)
- BEGIN
- if debug then writeln(bugfil, 'rdata');
- if numtry > MAXTRY then (* abort if too many tries *)
- rdata := 'A'
- else
- BEGIN
- numtry := numtry + 1; (* bump try counter *)
- (* get packet *) case rpack(len, num, recpkt) of
- (* got a data packet *) 'D' : BEGIN
- (* looks like wrong number *) if num <> n then
- BEGIN
- (* if too many tries, then quit *) if oldtry > MAXTRY then
- rdata := 'A'
- else
- BEGIN
- (* bump oldtry counter *) oldtry := oldtry + 1;
- (* see if we have previous packet again *) k := n - 1;
- if k < 0 then k := 63;
- (* yes, got previous one *) if num = k then
- BEGIN
- (* re-ACK the packet *) spack('Y', num,
- 0, packet);
- (* reset try counter *) numtry := 0;
- (* stay in D, don't write out data *) rdata := state;
- END
- else
- (* Sorry, wrong number *) rdata := 'A';
- END;
- END; { num <> n }
- (* write the packet to file *) bufemp(recpkt, len);
- (* acknowledge the packet *) spack('Y', n, 0, packet);
- (* reset the counters *) oldtry := numtry;
- numtry := 0;
- (* count packets, mod 64 *) n := (n + 1) mod 64;
- (* stay in this state *) rdata := 'D';
- END; {D}
- (* got a file header *) 'F' : BEGIN
- (* too many, so quit *) if oldtry > MAXTRY then
- rdata := 'A'
- else
- BEGIN
- (* bump try counter *) oldtry := oldtry + 1;
- (* see if previous packet *) k := n - 1;
- if k < 0 then k := 63;
- (* yes, so ACK it again *) if num = k then
- BEGIN
- spack('Y', num, 0,
- packet);
- numtry := 0;
- (* stay in data state *) rdata := state;
- END
- else
- (* not previous packet so abort *) rdata := 'A';
- END;
- END; {Z}
- 'Z' : BEGIN
- (* must have right packet *) if num <> n then
- rdata := 'A'
- else
- BEGIN
- (* OK, so ACK it *) spack('Y', n, 0, packet);
- (* close the file *) close(rcvfil);
- (* bump packet counter *) n := (n + 1) mod 64;
- (* go back to receive file state *) rdata := 'F';
- END;
- END;
- (* nothing, keep waiting *) 'E' : rdata := state;
- (* some other type, *) otherwise
- (* so abort *) rdata := 'A';
- END; {case}
- END;
- END; {rdata}
-
- BEGIN {recsw}
- done := false; (* initialize *)
- state := 'R'; (* always start in receive state *)
- n := 0; (* initialize message number *)
- numtry := 0; (* no tries yet *)
- while not done do (* do until done *)
- case state of
- 'D' : state := rdata; (* data receive state *)
- 'F' : state := rfile; (* file receive state *)
- 'R' : state := rinit; (* send initiate state *)
- 'C' : BEGIN (* completed state *)
- recsw := true;
- done := true;
- END;
- 'A' : BEGIN (* abort state *)
- recsw := false;
- done := true;
- END;
- END; {case}
- END; {recsw}
- %page
- FUNCTION sendsw : boolean;
- (* State table switcher for sending files *)
- VAR
- done : boolean; (* indicates that sending is finished *)
-
- FUNCTION sinit : char;
- (* Send my parameters and get other side's back *)
- VAR
- num, len : integer; (* packet number, length *)
- BEGIN {function sinit}
- if debug then writeln(bugfil, 'sinit');
- if numtry > MAXTRY then sinit := 'A' (* too many tries *)
- else
- BEGIN
- numtry := numtry + 1; (* bump try counter *)
- spar(packet); (* fill up with init info *)
- spack('S', n, 6, packet); (* send it out *)
- case rpack(len, num, recpkt) of (* get reply *)
- (* NAK packet *) 'N', 'E' : sinit := state; (* just stay in state *)
- (* ACK packet *) 'Y' : BEGIN
- (* wrong ACK, stay in state *) if n <> num then
- sinit := state
- else
- BEGIN
- (* get other side's init info *) rpar(recpkt);
- (* check and set defaults *) if eol = chr(NUL)
- then eol := chr(CR);
- if quote = chr(NUL)
- then quote := MYQUOTE;
- (* reset try counter *) numtry := 0;
- (* bump packet count *) n := (n + 1) mod 64;
- (* open file to be sent *) if getnxt then
- (* if open OK go to next state *) sinit := 'F'
- (* no good, so give up *) else sinit := 'A';
- END; {else}
- END; {'Y'}
- (* unknown, abort *) otherwise
- sinit := 'A';
- END; {case}
- END; {else}
- END; {sinit}
- %page
- FUNCTION sfile : char;
- (* Send file name *)
- VAR
- num, len, l : integer; (* packet number, len, stringlength *)
- c : char; (* utility character *)
- BEGIN
- if debug then writeln(bugfil, 'sfile');
- if numtry > MAXTRY (* too many tries, give up *)
- then sfile := 'A'
- else
- BEGIN
- numtry := numtry + 1; (* bump try counter *)
- len := 0; (* set packet length to zero *)
- l := length(filnam[numsent]); (* length of filename *)
- while (len < l) and (len < NAMESIZE) do
- BEGIN
- len := len + 1; (* accumulate length *)
- (* stash away the name itself *) packet@[len] :=
- (* in upper case *) toupper(filnam[numsent][len]);
- END;
- (* send it out *) spack('F', n, len, packet);
- (* get reply *) c := rpack(len, num, recpkt);
- case c of
- (* NAK or ACK *) 'N', 'Y' : BEGIN
- if c = 'N' then
- (* as before, stay in this state *) BEGIN
- (* unless NAK for next packet *) num := num - 1;
- (* which is like an ACK for this packet *) if num < 0 then num := 63;
- END;
- (* wrong count so stay in this state *) if n <> num then sfile := state
- else
- BEGIN
- (* reset counters *) numtry := 0;
- (* bump packet count *) n := (n + 1) mod 64;
- (* get first data from file *) size := bufill(packet);
- (* switch to data state *) sfile := 'D';
- END;
- END;
- (* receive failure *) 'E' : sfile := state; (* just stay here *)
- otherwise
- (* unknown, abort *) sfile := 'A';
- END; {case}
- END; {else}
- END; {sinit}
- %page
- FUNCTION sdata : char;
- VAR
- num, len : integer; (* packet number, length *)
- c : char; (* utility character *)
- BEGIN
- if debug then writeln(bugfil, 'sdata');
- if numtry > MAXTRY then sdata := 'A' (* abort if too many *)
- else
- BEGIN
- numtry := numtry + 1; (* bump try counter *)
- spack('D', n, size, packet); (* send a data packet *)
- c := rpack(len, num, recpkt); (* get the reply *)
- case c of
- 'N', 'Y' : BEGIN (* NAK or ACK *)
- (* respond to NAK *) if c = 'N' then
- BEGIN
- num := num - 1;
- if num < 0 then num := 63;
- END;
- (* just stay in this state *) if n <> num then sdata := state
- (* unless NAK is for next packet *) else
- (* which is like an ACK for this one *) BEGIN
- (* reset try counter *) numtry := 0;
- (* bump packet count *) n := (n + 1) mod 64;
- if not eof(sndfil) then
- BEGIN
- (* get data from file if not at end *) size :=
- bufill(packet);
- (* stay in data state *) sdata := 'D';
- END
- else
- (* EOF, so switch to that state *) sdata := 'Z';
- END;
- END;
- (* receive failure *) 'E' : sdata := state; (* stay in state *)
- otherwise
- (* anything else, abort *) sdata := 'A';
- END; {case}
- END; {else}
- END; {sdata}
- %page
- FUNCTION seof : char;
- (* Send enf-of-file *)
- VAR
- num, len : integer; (* packet number, length *)
- c : char; (* utility char *)
- BEGIN
- if debug then writeln(bugfil, 'seof');
- if numtry > MAXTRY then (* too many, quit *)
- seof := 'A'
- else
- BEGIN
- numtry := numtry + 1; (* bump counter *)
- spack('Z', n, 0, packet); (* send Z packet *)
- c := rpack(len, num, recpkt); (* get reply *)
- case c of
- (* ACK or NAK *) 'N', 'Y' : BEGIN
- (* NAK, fail unless for *) if c = 'N' then
- (* previous packet *) BEGIN
- (* then fall thru *) num := num - 1;
- if num < 0 then num := 63;
- END;
- (* wrong, so stay in state *) if n <> num then seof := state
- else
- BEGIN
- (* reset counter *) numtry := 0;
- (* increment count *) n := (n + 1) mod 64;
- if debug then
- writeln(bugfil,
- 'closing - ',
- filnam[numsent]);
- (* close the file *) close(sndfil);
- (* increment number of files sent *) numsent := numsent + 1;
- (* get new one if more to go *) if numsent < nfiles then
- BEGIN
- (* and go back to filename state *) if getnxt then
- seof := 'F'
- else
- (* unless failure in file open *) seof := 'B'
- END
- (* no more files, so set break state *) else seof := 'B';
- END; {else}
- END; {N, Y}
- (* error, stay in state *) 'E' : seof := state;
- (* unknown, abort *) otherwise
- seof := 'A';
- END; {case}
- END; { else }
- END; {seof}
- %page
- FUNCTION sbreak : char;
- (* send a break *)
- VAR
- num, len : integer; (* packet number, length *)
- c : char; (* utility char *)
- BEGIN
- if debug then writeln(bugfil, 'sbreak');
- if numtry > MAXTRY then
- sbreak := 'A' (* abort if too many *)
- else
- BEGIN
- (* bump counter *) numtry := numtry + 1;
- (* send a break *) spack('B', n, 0, packet);
- (* look at reply *) c := rpack(len, num, recpkt);
- case c of
- (* see if ACK for this *) 'N', 'Y' : BEGIN
- (* packet or NAK for previous *) if c = 'N' then
- BEGIN
- num := num - 1;
- if num < 0 then num := 63;
- END;
- (* if wrong, then stay in state *) if n <> num then sbreak := state
- else
- BEGIN
- (* reset counter *) numtry := 0;
- (* bump packet count *) n := (n + 1) mod 64;
- (* switch to complete state *) sbreak := 'C';
- END;
- END;
- (* receive failure *) 'E' : sbreak := state; (* stay in state *)
- otherwise
- (* unknown, abort *) sbreak := 'A';
- END; {case}
- END; { else }
- END; {sbreak}
- %page
- BEGIN {sendsw}
- done := false; (* not done yet *)
- state := 'S'; (* send initiate is the start state *)
- n := 0; (* initialize message number *)
- numtry := 0; (* no tries yet *)
- while not done do
- case state of
- 'D' : state := sdata; (* data send state *)
- 'F' : state := sfile; (* send file name *)
- 'Z' : state := seof; (* end of file *)
- 'S' : state := sinit; (* send-init *)
- 'B' : state := sbreak; (* break-send *)
- 'C' : BEGIN sendsw := true; done := true END;
- (* complete *)
- 'A' : BEGIN sendsw := false; done := true END;
- (* abort *)
- otherwise
- BEGIN sendsw := false; done := true END;
- (* unknown, so fail *)
- END; {case}
- END; {sendsw}
- %page
- PROCEDURE init; (* Initialize parameters *)
- BEGIN
- delay[1] := 0; (* set up initial packet delay *)
- delay[2] := SNDINIT_DLY;
- ascii := false; (* We are using ASCII if true *)
- debug := false; (* For program development *)
- if debug then (* creating temporary debug file *)
- BEGIN
- (* cmdnoe('$create -debug', 14); *)
- rewrite(bugfil, 'FILE=-debug');
- END;
- reset(input, 'FILE=*msource* Interactive MAXLEN=255');
- rewrite(output, 'FILE=*msink* MAXLEN=255');
- (* make wide as possible *)
- new(packet); (* Point to packet *)
- new(recpkt); (* make the space needed *)
- eol := chr(CR); (* EOL for outgoing packets *)
- quote := MYQUOTE; (* Standard control-quote char *)
- pad := 0; (* No padding *)
- padchar := chr(NUL); (* Use null if any padding wanted *)
- END;
- %page
- BEGIN {main}
- datetime(date, time);
- writeln('Mathematical Reviews - Kermit on MTS.');
- writeln('The date is ', date, '. The time is ', time, '.');
- writeln;
- writeln('For help see the file SJ1K:KERMIT.DOC.');
- writeln;
- init; (* initialize all parameters *)
- writeln('Enter command - (r)eceive/(s)end:');
- readln(command); (* get the command *)
- command := toupper(command); (* convert to upper case *)
- writeln('Is column 1 reserved for carriage control (y/n)?');
- readln(ccinfo);
- cc := (toupper(ccinfo) = 'Y');
- if command = 'S' then (* get the files to send *)
- BEGIN
- nfiles := 0;
- writeln('Enter file names one at a time.');
- writeln('Terminate list with carriage return.');
- writeln;
- repeat
- writeln('File to send:');
- nfiles := nfiles + 1;
- readln(filnam[nfiles]);
- until (nfiles >= MAXFILES) or (filnam[nfiles] = '')
- END;
- setsys; (* set the terminal so Kermit will work *)
- case command of
- 'S' : BEGIN (* send files *)
- writeln;
- write('Exit to your system, set IBM mode ON,');
- writeln(' and initiate RECEIVE-FILE mode.');
- writeln(chr(DC1)); (* write an XON *)
- twait(0, delay); (* wait a while *)
- numsent := 1; (* none sent yet *)
- if sendsw = false then (* now go to send switcher *)
- if debug then
- writeln(bugfil, 'Send failed at - ',
- filnam[numsent])
- else if debug then writeln(bugfil, 'Send OK');
- END;
- 'R' : BEGIN (* receive files *)
- writeln;
- write('Exit to your system, set IBM mode ON,');
- writeln(' and initiate SEND-FILE mode.');
- if recsw = false then (* go to receive state switcher *)
- if debug then writeln(bugfil, 'Receive failed.')
- else if debug then writeln(bugfil, 'Receive OK.');
- END;
- otherwise (* not a valid command *)
- writeln('Invalid command given.');
- END; {case}
- close(bugfil);
- resetsys; (* return terminal to original state *)
- END. {Kermit}
-